import numpy as np
import pinocchio
from numpy.linalg import norm, solve

model = pinocchio.buildSampleModelManipulator()
data = model.createData()

# end effector 是第六个关节
JOINT_ID = 6

# 期望的姿态如下（SE3）
oMdes = pinocchio.SE3(np.eye(3), np.array([1.0, 0.0, 1.0]))

# 设置初始参数
q = pinocchio.neutral(model)

# 设定部分计算相关常量
eps = 1e-4
IT_MAX = 1000
DT = 1e-1
damp = 1e-12

i = 0
while True:
    pinocchio.forwardKinematics(model, data, q)
    # dMi corresponds to the transformation between the desired pose and the current one
    iMd = data.oMi[JOINT_ID].actInv(oMdes)

    # 通过对数映射将李群SE(3)空间中的4x4变换矩阵映射为一个六维的se(3)李代数向量，再求这个转动向量的范数
    err = pinocchio.log(iMd).vector # in joint frame
    if norm(err) < eps:
        success = True
        break
    if i >= IT_MAX:
        success = False
        break

    # 计算基坐标到JOINT_ID的Jacobian
    J = pinocchio.computeJointJacobian(model, data, q, JOINT_ID) # in joint frame
    # 得到IK需要的Jacobian，即表示error的Jacobian，https://scaron.info/robotics/jacobian-of-a-kinematic-task-and-derivatives-on-manifolds.html
    J = -np.dot(pinocchio.Jlog6(iMd.inverse()), J)
    
    # 为了避免出现奇异，这里先计算error Jacobian的damped pseudo-inverse
    v = -J.T.dot(solve(J.dot(J.T) + damp * np.eye(6), err))
    # 然后基于error Jacobian的伪逆（v），去迭代q
    q = pinocchio.integrate(model, q, v * DT)


if success:
    print("Convergence achieved!")
else:
    print(
        "\n"
        "Warning: the iterative algorithm has not reached convergence "
        "to the desired precision"
    )

print(f"\nresult: {q.flatten().tolist()}")
print(f"\nfinal error: {err.T}")



print('type of q: ', type(q))
print('type of v: ', type(v))
print('type of DT: ', type(DT))